﻿using System;
using System.IO;
using System.Collections.Generic;

namespace Narwhunderful.Aphelia.Framework.IO.Serialization.Binary
{

    public static class BinaryReaderHelpers
    {

        public static string ReadString(BinaryReader reader)
        {
            if (!reader.ReadBoolean()) return null;
            return reader.ReadString();
        }

        public static int ReadNumber(BinaryReader reader)
        {
            // Size
            byte size = reader.ReadByte();

            // Number
            switch (size)
            {
                case NumberSize.Zero:
                    return 0;
                case NumberSize.B1:
                    return reader.ReadByte();
                case NumberSize.B2:
                    return reader.ReadInt16();
                default:
                    return reader.ReadInt32();
            }
        }

        public static int[] ReadNumbers(BinaryReader reader)
        {
            // Count
            int count = ReadNumber(reader);

            if (count == 0) return new int[0];

            // Items
            var result = new List<int>();
            for (int i = 0; i < count; i++)
            {
                result.Add(ReadNumber(reader));
            }
            return result.ToArray();
        }

        public static object ReadValue(Type expectedType, BinaryReader reader)
        {
            if (!reader.ReadBoolean()) return null;
            return readValueCore(expectedType, reader);
        }

        private static object readValueCore(Type type, BinaryReader reader)
        {
            try
            {
                if (type == typeof(byte[])) return ReadArrayOfByte(reader);
                if (type == typeof(string)) return reader.ReadString();
                if (type == typeof(Boolean)) return reader.ReadBoolean();
                if (type == typeof(Byte)) return reader.ReadByte();
                if (type == typeof(Char)) return reader.ReadChar();
                if (type == typeof(DateTime)) return new DateTime(reader.ReadInt64());
                if (type == typeof(Guid)) return new Guid(reader.ReadBytes(16));
#if (SMARTPHONE || SILVERLIGHT || XBOX)
#else
                if (type == typeof(Decimal)) return reader.ReadDecimal();
#endif
                if (type == typeof(Double)) return reader.ReadDouble();
                if (type == typeof(Int16)) return reader.ReadInt16();
                if (type == typeof(Int32)) return reader.ReadInt32();
                if (type == typeof(Int64)) return reader.ReadInt64();
                if (type == typeof(SByte)) return reader.ReadSByte();
                if (type == typeof(Single)) return reader.ReadSingle();
                if (type == typeof(UInt16)) return reader.ReadUInt16();
                if (type == typeof(UInt32)) return reader.ReadUInt32();
                if (type == typeof(UInt64)) return reader.ReadUInt64();

                if (type == typeof(TimeSpan)) return new TimeSpan(reader.ReadInt64());

                // Enumeration
                if (type.IsEnum) return ReadEnumeration(type, reader);

                throw new InvalidOperationException(string.Format("Unknown simple type: {0}", type.FullName));
            }
            catch (Exception ex)
            {
                throw new SimpleValueParsingException(
                    string.Format("Invalid type: {0}. See details in the inner exception.", type), ex);
            }
        }

        private static object ReadEnumeration(Type expectedType, BinaryReader reader)
        {
            // read the enum as int
            int value = reader.ReadInt32();
            object result = Enum.ToObject(expectedType, value);
            return result;
        }

        private static byte[] ReadArrayOfByte(BinaryReader reader)
        {
            int length = ReadNumber(reader);
            if (length == 0) return null;

            return reader.ReadBytes(length);
        }
    }
}
